package com.piece.core.pay.client.impl.wxpay;

import com.piece.core.framework.exception.ErrorCode;
import com.piece.core.framework.support.response.AjaxResponse;
import com.piece.core.framework.util.collection.MapUtil;
import com.piece.core.framework.util.object.ObjectUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.time.DateUtil;
import com.piece.core.pay.dto.*;
import com.piece.core.pay.enums.PayChannelType;
import com.piece.core.pay.exception.PayConfigException;
import com.wechat.pay.java.core.RSAConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.util.IOUtil;
import com.wechat.pay.java.core.util.PemUtil;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.weixinpayscanandride.WeixinPayScanAndRideService;
import lombok.extern.slf4j.Slf4j;

/**
 * 微信JsAPI支付实现类
 */
@Slf4j
public class WXJsPayClient extends AbstractWxPayClient {

    private JsapiServiceExtension service;

    public WXJsPayClient(String channelId, WXPayClientConfig config) {
        super(channelId, PayChannelType.WX_PUB.getCode(), config);
    }

    @Override
    protected void doInit() {
        try {
            RSAConfig rsaConfig = new RSAConfig.Builder()
                    .merchantId(config.getMchId())
                    .privateKey(PemUtil.loadPrivateKeyFromString(IOUtil.toString(config.getPrivateKey())))
                    .merchantSerialNumber(getSerialNumber(config.getPrivateCert()))
                    .wechatPayCertificates(IOUtil.toString(config.getPrivateCert()))
                    .build();
            service = new JsapiServiceExtension.Builder().config(rsaConfig).build();
            refundService = new RefundService.Builder().config(rsaConfig).build();
            rideService = new WeixinPayScanAndRideService.Builder().config(rsaConfig).build();
        } catch (Exception e) {
            throw new PayConfigException();
        }
    }

    /**
     * 下单实现接口
     */
    @Override
    public AjaxResponse doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
        PrepayWithRequestPaymentResponse response;
        try {
            response = unifiedOrderResponse(reqDTO);
        } catch (HttpException e) { // 发送HTTP请求失败
            log.error("[微信jsApi支付失败] 原因：{}", e);
            return AjaxResponse.result(ErrorCode.HTTP, "微信支付失败");
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            log.error("[微信jsApi支付失败] 原因：{}", e);
            return AjaxResponse.result(codeMapping.apply(
                    ObjectUtil.defaultIfNull(e.getErrorCode(), "CustomErrorCode"),
                    ObjectUtil.defaultIfNull(e.getErrorMessage())), null);
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            log.error("[微信jsApi支付失败] 原因：{}", e);
            return AjaxResponse.result(ErrorCode.ARGUMENT, "微信支付失败");
        }

        return AjaxResponse.result(codeMapping.apply(WXResponseMapping.CODE_SUCCESS, WXResponseMapping.MESSAGE_SUCCESS), response);
    }

    private PrepayWithRequestPaymentResponse unifiedOrderResponse(PayOrderUnifiedReqDTO reqDTO) {
        PrepayRequest request = new PrepayRequest();
        request.setOutTradeNo(reqDTO.getMerchantOrderId());
        request.setDescription(reqDTO.getBody());
        // 设置金额,单位分
        Amount amount = new Amount();
        amount.setTotal(reqDTO.getAmount().intValue());
        request.setAmount(amount);
        request.setTimeExpire(DateUtil.formatDate(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"));
        // 设置消费者
        Payer payer = new Payer();
        payer.setOpenid(getOpenid(reqDTO));
        request.setPayer(payer);
        // 设置场景
        SceneInfo sceneInfo = new SceneInfo();
        sceneInfo.setPayerClientIp(reqDTO.getUserIp());
        request.setSceneInfo(sceneInfo);
        request.setNotifyUrl(StringUtil.isEmpty(reqDTO.getNotifyUrl()) ? config.getNotifyUrl() : reqDTO.getNotifyUrl());
        // 执行请求
        return service.prepayWithRequestPayment(request);
    }

    private static String getOpenid(PayOrderUnifiedReqDTO reqDTO) {
        String openid = MapUtil.getString(reqDTO.getChannelExtras(), "openid");
        if (StringUtil.isEmpty(openid)) {
            throw new IllegalArgumentException("微信支付请求的 openid 不能为空！");
        }
        return openid;
    }
}
